// tspcluster - Traveling Salesman Problem
// Copyright (C) 2006  Frederik Carlier
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

using System;
using System.IO;

namespace UGent.Tsp
{
	public class WangLandau
	{		
		public WangLandau(MarkovCluster cluster)
		{
		    this.cluster = cluster;
		}
		
		private MarkovCluster cluster;
		
		public void Run()
		{
            int run = 0;
            double f = Math.E;
            bool isFlat = true;
            
            // Create a flat distribution;
            Distribution distribution = new Distribution(cluster.HistogramLower, cluster.HistogramLowerExtended, cluster.HistogramUpper, cluster.HistogramUpperExtended, cluster.HistogramNumberOfBins);
             
            while (true)
            {
                run++;
                
                string fileName = string.Format("run-{0}-distribution.txt", run);
                if(File.Exists(fileName))
                    File.Delete(fileName);
                
                using(FileStream stream = File.OpenWrite(fileName))
                {
                    distribution.WriteToStream(stream);
                }

                // Sample according to the inverse of the distribution
                if(isFlat)
                {
                    cluster.SetAlgorithmType(AlgorithmType.WangLandau);
                    cluster.SetDistribution(distribution);
                    cluster.SetWangLandauParameter(f);

                    cluster.Start();
                }
                else
                {
                    cluster.Resume();
                }
                
                cluster.WaitForAll();

                Results results = cluster.GetResults();
                Distribution histogram = results.Transitions.GetDistribution(DistributionEstimator.Histogram, false);
                
                // Normalize
                distribution.Normalize();
                
                // Write out a bunch of less or more interesting things.
                fileName = string.Format("run-{0}-histogram.txt", run);
                if(File.Exists(fileName))
                    File.Delete(fileName);
                
                using(FileStream stream = File.OpenWrite(fileName))
                {
                    histogram.WriteToStream(stream);
                }
                
                // Get a indication for the "flatness" of the histogram
                Results flatnessResults = new Results(0, 0);
                for(int i = 0; i < histogram.Count; i++)
                {
                    if(histogram[i] != 0)
                        flatnessResults.Add(histogram[i]);
                }
                
                
                double upper = 1.2 * flatnessResults.GetAverage();
                double lower = 0.8 * flatnessResults.GetAverage();
                
                for(int i = 0; i < histogram.Count; i++)
                {
                    if(histogram[i] != 0)
                    {
                        if(histogram[i] < lower || histogram[i] > upper)
                        {
                            isFlat = false;
                            break;
                        }
                    }
                }
                
                if(isFlat)
                {
                    Console.WriteLine("Was flat!");
                    f = Math.Sqrt(f);
                    // Update the distribution
                    for(int i = 0; i < histogram.Count; i++)
                    {
                        distribution[i] += Math.Log(f) * histogram[i];
                    }
                }
                else
                {
                    Console.WriteLine("Wasn't flat!");
                }

            }		    
		}
		
		public static void RunOnConsole()
		{
		    MarkovCluster cluster = new MarkovCluster();
		    cluster.ConfigureOnConsole();
		    cluster.ConfigureSimulationOnConsole();
		    WangLandau wl = new WangLandau(cluster);
		    wl.Run();
		}
	}
}
